
/**************************************
*                                     *
*   Jeff Molofee's Basecode Example   *
*   SDL porting by Fabio Franchello   *
*          nehe.gamedev.net           *
*                2001                 *
*                                     *
**************************************/

// Includes
#ifdef WIN32													// If We're Under MSVC
#include <windows.h>											// Include The Windows Header
#else															// Otherwise
#include <stdio.h>												// Include The Standar IO Header
#include <stdlib.h>												// And The Standard Lib (for exit())
#endif

#include <deque>                                                // STL container
#include <math.h>

#include "glall.h"												// Including All OpenGL Headers

#include "texture.h"												// Including Texture Definition

#include "main.h"												// We're Including the Header Where Defs And Prototypes Are


bool isReady = false;                                           // GL initialized and ready for action

extern S_AppStatus AppStatus;									// We're Using This Struct As A Repository For The Application State (Visible, Focus, ecc)

int currentWidth = 0;											// Viewport width
int currentHeight = 0;											// Viewport height

const unsigned int millisecondsBetweenCounters = 250;           // Refresh framerate 4 times per second.
unsigned int allTrianglesEver = 0;                              // Number of triangles rendered

// User Defined Structures
typedef struct tagMATRIX										// A Structure To Hold An OpenGL Matrix
{
	float Data[16];												// We Use [16] Due To OpenGL's Matrix Format
}
MATRIX;

typedef struct tagVECTOR										// A Structure To Hold A Single Vector
{
	float X, Y, Z;												// The Components Of The Vector
}
VECTOR;

typedef struct tagVERTEX										// A Structure To Hold A Single Vertex
{
	VECTOR Nor;													// Vertex Normal
	VECTOR Pos;													// Vertex Position
}
VERTEX;

typedef struct tagPOLYGON										// A Structure To Hold A Single Polygon
{
	VERTEX Verts[3];											// Array Of 3 VERTEX Structures
}
POLYGON;

// User Defined Variables
bool		outlineDraw		= true;								// Flag To Draw The Outline
bool		outlineSmooth	= false;							// Flag To Anti-Alias The Lines
float		outlineColor[3]	= { 0.0f, 0.0f, 0.0f };				// Color Of The Lines
float		outlineWidth	= 3.0f;								// Width Of The Lines

VECTOR		lightAngle;											// The Direction Of The Light
GLfloat		lightPos[4];										// The Direction Of The Light, Stored In Four Components Because glLight Needs Them (NEW)
bool		lightRotate		= false;							// Flag To See If We Rotate The Light

float		modelAngle		= 0.0f;								// Y-Axis Angle Of The Model
bool        modelRotate		= false;							// Flag To Rotate The Model

POLYGON		*polyData		= NULL;								// Polygon Data
int			polyNum			= 0;								// Number Of Polygons

GLuint		shaderTexture[1];									// Storage For One Texture

bool        useBufferObject = true;                             // Flag To Use Vertex Buffer Objects (Unused if Vertex Buffer Objects Are Not Supported) ( NEW )
GLuint      polyBufferObj[1];                                   // Storage For One Vertex Buffer Object

bool        useVertexProgram= true;								// Flag To Use Vertex Programs (Unused if Vertex Programs Are Not Supported) ( NEW )
bool        useFragmentProgram= true;							// Flag To Use Fragment Programs (Unused if Fragment Programs Are Not Supported) ( NEW )
GLuint      program[3];											// Storage For Three Program : Two Vertex Programs and One Fragment Program ( NEW )

Texture		msgFP;
Texture		msgVP;
Texture		msgVBO;
Texture		msgUnavail;
Texture		msgEnabled;
Texture		msgDisabled;
Texture		msgOutline[2];
Texture		msgVertex[2];
Texture		msgShading[3];
Texture		msgRemark[2];

GLubyte     vertexProgramStringPerVertex[] =					// The First Vertex Program ( NEW )
"!!ARBvp1.0\
#\n\
# This computes the dot product between the normal and the\n\
# light direction and outputs it to the S texture coord\n\
#\n\
ATTRIB iPos         = vertex.position;\
ATTRIB iColor		= vertex.color;\
ATTRIB iNormal      = vertex.normal;\
PARAM  mvinv[4]     = { state.matrix.modelview.invtrans };\
PARAM  mvp[4]       = { state.matrix.mvp };\
PARAM  lightDir     = state.light[0].position;\
TEMP   xfNormal, tempdot;\
OUTPUT oPos         = result.position;\
OUTPUT oColor       = result.color;\
OUTPUT oTex         = result.texcoord;\
\
# Transform the vertex to clip coordinates.\n\
# We transform succesively through the modelview matrix, then the projection matrix.\n\
DP4 oPos.x, mvp[0], iPos;\
DP4 oPos.y, mvp[1], iPos;\
DP4 oPos.z, mvp[2], iPos;\
DP4 oPos.w, mvp[3], iPos;\
\
# Transform the normal to eye coordinates.\n\
# We transform through the inverse transpose modelview matrix, not through the modelview matrix (see OpenGL specs).\n\
DP3 xfNormal.x, mvinv[0], iNormal;\
DP3 xfNormal.y, mvinv[1], iNormal;\
DP3 xfNormal.z, mvinv[2], iNormal;\
\
# Compute the dot product between normal and light position (light direction in fact, since the w component is 0).\n\
DP3 tempdot.x, xfNormal, lightDir;\
# Store the dot product result into S texcoord, and clamp it to zero.\n\
MAX oTex.x, tempdot.x, 0;\
\
# Write our color out directly\n\
MOV oColor, iColor;\
\
END";
GLubyte     vertexProgramStringPerPixel[] =					// The Second Vertex Program ( NEW )
"!!ARBvp1.0\
#\n\
# This sends the normal and light direction in eye-space\n\
# so that the fragment program can then take them and compute lighting per-pixel\n\
#\n\
ATTRIB iPos         = vertex.position;\
ATTRIB iColor		= vertex.color;\
ATTRIB iNormal      = vertex.normal;\
PARAM  mvinv[4]     = { state.matrix.modelview.invtrans };\
PARAM  mvp[4]       = { state.matrix.mvp };\
PARAM  lightDir     = state.light[0].position;\
TEMP   xfNormal, tempdot;\
OUTPUT oPos         = result.position;\
OUTPUT oColor       = result.color;\
OUTPUT oTex         = result.texcoord[0];\
OUTPUT oTex2        = result.texcoord[1];\
\
# Transform the vertex to clip coordinates.\n\
# We transform succesively through the modelview matrix, then the projection matrix.\n\
DP4 oPos.x, mvp[0], iPos;\
DP4 oPos.y, mvp[1], iPos;\
DP4 oPos.z, mvp[2], iPos;\
DP4 oPos.w, mvp[3], iPos;\
\
# Transform the normal to eye coordinates.\n\
# We transform through the inverse transpose modelview matrix, not through the modelview matrix (see OpenGL specs).\n\
DP3 xfNormal.x, mvinv[0], iNormal;\
DP3 xfNormal.y, mvinv[1], iNormal;\
DP3 xfNormal.z, mvinv[2], iNormal;\
MOV xfNormal.w, 1;\
\
# Send the normal into texcoord[0]\n\
MOV oTex, xfNormal;\
\
# Send the light direction into texcoord[1]\n\
MOV oTex2, lightDir;\
\
# Write our color out directly\n\
MOV oColor, iColor;\
\
END";
GLubyte     fragmentProgramStringPerPixel[] =					// The Fragment Program ( NEW )
"!!ARBfp1.0\
#\n\
# This computes the dot product between the normal and the\n\
# light direction and samples it to the S texture coord\n\
#\n\
ATTRIB normalVP = fragment.texcoord[0];\
ATTRIB lightDirVP = fragment.texcoord[1];\
ATTRIB col = fragment.color.primary;\
\
OUTPUT outColor = result.color;\
\
TEMP texel1D, texCoord1D;\
TEMP normalizedNormal, normalizedLightDir;\
\
# Normalize the input vectors\n\
DP3 normalizedNormal.w, normalVP, normalVP;\
RSQ normalizedNormal.w, normalizedNormal.w;\
MUL normalizedNormal.xyz, normalizedNormal.w, normalVP;\
\
DP3 normalizedLightDir.w, lightDirVP, lightDirVP;\
RSQ normalizedLightDir.w, normalizedLightDir.w;\
MUL normalizedLightDir.xyz, normalizedLightDir.w, lightDirVP;\
\
# Compute the dot product between normal and light position (light direction in fact, since the w component is 0).\n\
DP3 texCoord1D, normalizedNormal, normalizedLightDir;\
\
# Store the dot product result into S texcoord, and clamp it to zero.\n\
MAX texCoord1D.x, texCoord1D.x, 0;\
MOV texCoord1D.w, 1;\
\
# Sample the 1D texture\n\
TXP texel1D, texCoord1D, texture, 1D;\
\
MUL outColor, texel1D, col;\
\
END";

// File Functions
bool ReadMesh ()												// Reads The Contents Of The "model.txt" File
{
	FILE *In = fopen ("Data/model.txt", "rb");					// Open The File

	if (!In)
    {
        Log("\nUnable to load model\n");

		return false;											// Return FALSE If File Not Opened
    }

	fread (&polyNum, sizeof (int), 1, In);						// Read The Header (i.e. Number Of Polygons)

	polyData = new POLYGON [polyNum];							// Allocate The Memory

	fread (&polyData[0], sizeof (POLYGON) * polyNum, 1, In);	// Read In All Polygon Data

	fclose (In);												// Close The File

    Log("\nModel loaded\n");

    // Loading the vertex buffer object
#if defined(GL_ARB_vertex_buffer_object)
    if (rt_ARB_vertex_buffer_object)
    {
		glGenBuffersARB(1, &polyBufferObj[0]);					// Generate A Vertex Buffer Object, Just Like We Would Generate A Texture ( NEW )

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, polyBufferObj[0]);	// Bind This Buffer Object, As A Vertex Buffer Object

		glBufferDataARB(GL_ARRAY_BUFFER_ARB,				    // Load The Vertex Buffer Object
						sizeof (POLYGON) * polyNum,
						&polyData[0],
						GL_STATIC_DRAW_ARB);

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);            	// Bind The Default Buffer Object

        Log("\nUsing vertex buffer objects\n");
    } else
#endif
    {
        Log("\nNot using vertex buffer objects\n");
        (void)0;
    }

	return true;												// It Worked
}

// Texture log
void LogTextureError(Texture::LoadingState state)
{
	switch (state)
	{
		case Texture::ErrorNullFilename :
			Log("Null file name\n");
			break;
		case Texture::ErrorFileNotFound :
			Log("File not found\n");
			break;
		case Texture::ErrorUnknownFormat :
			Log("Unknown format\n");
			break;
		case Texture::ErrorFileCorrupted :
			Log("File corrupted\n");
			break;
		default :
			Log("Other texture state\n");
			break;
	}
}

// Texture initialization
bool ReadTextures ()
{
	Log("\nMounting textures\n");
	msgVP.setFilename("Data/ARBvp.bmp");
	msgFP.setFilename("Data/ARBfp.bmp");
	msgVBO.setFilename("Data/ARBvbo.bmp");
	msgUnavail.setFilename("Data/unavail.bmp");
	msgEnabled.setFilename("Data/enabled.bmp");
	msgDisabled.setFilename("Data/disabled.bmp");
	msgOutline[0].setFilename("Data/outline0.bmp");
	msgOutline[1].setFilename("Data/outline1.bmp");
	msgVertex[0].setFilename("Data/vertex0.bmp");
	msgVertex[1].setFilename("Data/vertex1.bmp");
	msgShading[0].setFilename("Data/shading0.bmp");
	msgShading[1].setFilename("Data/shading1.bmp");
	msgShading[2].setFilename("Data/shading2.bmp");
	msgRemark[0].setFilename("Data/remark0.bmp");
	msgRemark[1].setFilename("Data/remark1.bmp");

	Log("\nSetting texture parameters\n");
	msgVP.setMipmaps(Texture::NoMipmap);
	msgFP.setMipmaps(Texture::NoMipmap);
	msgVBO.setMipmaps(Texture::NoMipmap);
	msgUnavail.setMipmaps(Texture::NoMipmap);
	msgEnabled.setMipmaps(Texture::NoMipmap);
	msgDisabled.setMipmaps(Texture::NoMipmap);
	msgOutline[0].setMipmaps(Texture::NoMipmap);
	msgOutline[1].setMipmaps(Texture::NoMipmap);
	msgVertex[0].setMipmaps(Texture::NoMipmap);
	msgVertex[1].setMipmaps(Texture::NoMipmap);
	msgShading[0].setMipmaps(Texture::NoMipmap);
	msgShading[1].setMipmaps(Texture::NoMipmap);
	msgShading[2].setMipmaps(Texture::NoMipmap);
	msgRemark[0].setMipmaps(Texture::NoMipmap);
	msgRemark[1].setMipmaps(Texture::NoMipmap);

	Log("Loading textures\n");
	Texture::LoadingState state;
	if ((state = msgVP.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("VP texture loaded\n");
	if ((state = msgFP.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("FP texture loaded\n");
	if ((state = msgVBO.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("VBO texture loaded\n");
	if ((state = msgUnavail.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Unavailable texture loaded\n");
	if ((state = msgEnabled.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Enabled texture loaded\n");
	if ((state = msgDisabled.load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Disabled texture loaded\n");
	if ((state = msgOutline[0].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	if ((state = msgOutline[1].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Outline textures loaded\n");
	if ((state = msgVertex[0].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	if ((state = msgVertex[1].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Vertex textures loaded\n");
	if ((state = msgShading[0].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	if ((state = msgShading[1].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	if ((state = msgShading[2].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Shading textures loaded\n");
	if ((state = msgRemark[0].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	if ((state = msgRemark[1].load()) != Texture::Finished)
	{
		LogTextureError(state);
		return false;
	}
	Log("Remark textures loaded\n");

	Log("Textures correctly initialized\n");
	return true;
}

// Math Functions
inline float DotProduct (VECTOR &V1, VECTOR &V2)				// Calculate The Angle Between The 2 Vectors
{
	return V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z;				// Return The Angle
}

inline float Magnitude (VECTOR &V)								// Calculate The Length Of The Vector
{
	return sqrt (V.X * V.X + V.Y * V.Y + V.Z * V.Z);			// Return The Length Of The Vector
}

void Normalize (VECTOR &V)										// Creates A Vector With A Unit Length Of 1
{
	float M = Magnitude (V);									// Calculate The Length Of The Vector

	if (M != 0.0f)												// Make Sure We Don't Divide By 0
	{
		V.X /= M;												// Normalize The 3 Components
		V.Y /= M;
		V.Z /= M;
	}
}

void RotateVector (MATRIX &M, VECTOR &V, VECTOR &D)				// Rotate A Vector Using The Supplied Matrix
{
	D.X = (M.Data[0] * V.X) + (M.Data[4] * V.Y) + (M.Data[8]  * V.Z);	// Rotate Around The X Axis
	D.Y = (M.Data[1] * V.X) + (M.Data[5] * V.Y) + (M.Data[9]  * V.Z);	// Rotate Around The Y Axis
	D.Z = (M.Data[2] * V.X) + (M.Data[6] * V.Y) + (M.Data[10] * V.Z);	// Rotate Around The Z Axis
}

// Code
void Reshape(int width, int height)								// Reshape The Window When It Is Resized
{
	currentWidth = width;
	currentHeight = height;

	glViewport(0,0,(GLsizei)(width),(GLsizei)(height));		// Reset The Current Viewport
	glMatrixMode(GL_PROJECTION);							// Select The Projection Matrix
	glLoadIdentity();										// Reset The Projection Matrix
    glFrustum(-((GLfloat)(width)/(GLfloat)(height))/2.f, +((GLfloat)(width)/(GLfloat)(height))/2.f, -0.5f, +0.5f, 1.0f, 100.0f);
//	gluPerspective(45.0f,(GLfloat)(width)/(GLfloat)(height),1.0f,100.0f);	

	glMatrixMode(GL_MODELVIEW);								// Select The Modelview Matrix
	glLoadIdentity();										// Reset The Modelview Matrix

	return;														// Always Return, We're Standard :)
}


bool InitRenderer()
{
    InitAllGL();
    isReady = true;                                             // Now GL is ready for action

/*	glClearColor(0.7f,0.4f,0.1f,0.5f);							// Black Background
	glClearStencil(0);											// Stencil Buffer Setup
	glClear(GL_STENCIL_BUFFER_BIT);		                        // Clear Stencil Buffer
	glClearDepth(1.0f);											// Depth Buffer Setup
	glDepthFunc(GL_LEQUAL);										// The Type Of Depth Testing (Less Or Equal)
	glEnable(GL_DEPTH_TEST);									// Enable Depth Testing
	glShadeModel(GL_SMOOTH);									// Select Smooth Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);			// Set Perspective Calculations To Most Accurate

    glFrontFace(GL_CCW);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
*/
	glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);			// Realy Nice perspective calculations
	
	glClearColor (0.7f, 0.7f, 0.7f, 0.0f);						// Light Grey Background
	glClearDepth (1.0f);										// Depth Buffer Setup

	glEnable (GL_DEPTH_TEST);									// Enable Depth Testing
	glDepthFunc (GL_LESS);										// The Type Of Depth Test To Do

	glShadeModel (GL_SMOOTH);									// Enables Smooth Color Shading
	glDisable (GL_LINE_SMOOTH);									// Initially Disable Line Smoothing

	glEnable (GL_CULL_FACE);									// Enable OpenGL Face Culling

	glDisable (GL_LIGHTING);									// Disable OpenGL Lighting


    // Loading the vertex program
#if defined(GL_ARB_vertex_program)
    if (rt_ARB_vertex_program)
    {
		glGenProgramsARB(3, &program[0]);						// Generate Programs, Just Like We Would Generate Textures ( NEW )

		glBindProgramARB(GL_VERTEX_PROGRAM_ARB, program[0]);	// Bind This Program, As A Vertex Program

		glProgramStringARB(GL_VERTEX_PROGRAM_ARB,				// Load The Vertex Program
						   GL_PROGRAM_FORMAT_ASCII_ARB,
						   strlen((const char*)vertexProgramStringPerVertex),
						   vertexProgramStringPerVertex);

        Log("\nUsing vertex programs\n");

#if defined(GL_ARB_fragment_program)
        if (rt_ARB_fragment_program)
        {
	    	glBindProgramARB(GL_VERTEX_PROGRAM_ARB, program[1]);	// Bind This Program, As A Vertex Program

    		glProgramStringARB(GL_VERTEX_PROGRAM_ARB,				// Load The Vertex Program
		    				   GL_PROGRAM_FORMAT_ASCII_ARB,
	    					   strlen((const char*)vertexProgramStringPerPixel),
    						   vertexProgramStringPerPixel);

    		glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program[2]);	// Bind This Program, As A Fragment Program

    		glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,				// Load The Vertex Program
	    					   GL_PROGRAM_FORMAT_ASCII_ARB,
		    				   strlen((const char*)fragmentProgramStringPerPixel),
			    			   fragmentProgramStringPerPixel);

            Log("\nUsing fragment programs\n");
        } else
#endif
        {
            Log("\nNot using fragment programs\n");
        }
    } else
#endif
    {
        Log("\nNot using vertex programs\n");
        Log("\nNot using fragment programs\n");
    }

    // Loading the cel-shading texture
    {
    	int i;														// Looping Variable
	    char Line[255];												// Storage For 255 Characters
	    float shaderData[32];										// Storate For The 96 Shader Values

    	FILE *In	= NULL;											// File Pointer

    	In = fopen ("Data/shader.txt", "r");						// Open The Shader File

	    if (In)														// Check To See If The File Opened
    	{
	    	for (i = 0; i < 32; i++)								// Loop Though The 32 Greyscale Values
    		{
			    if (feof (In))										// Check For The End Of The File
				    break;

		    	fgets (Line, 255, In);								// Get The Current Line

	    		shaderData[i] = atof (Line);						// Copy Over The Value
    		}

	    	fclose (In);											// Close The File
    	}

	    else
        {
            Log("\nUnable to load cel-shading texture\n");

    		return false;											// It Went Horribly Horribly Wrong
        }

	    glGenTextures (1, &shaderTexture[0]);						// Get A Free Texture 1D

    	glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);			// Bind This Texture. From Now On It Will Be 1D

	    // For Crying Out Loud Don't Let OpenGL Use Bi/Trilinear Filtering!
    	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);	
	    glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    	glTexImage1D (GL_TEXTURE_1D, 0, GL_LUMINANCE, 32, 0, GL_LUMINANCE , GL_FLOAT, shaderData);	// Upload

        Log("\nCel-shading texture loaded\n");
    }

    // Setup the light
    {
	    lightAngle.X = 0.0f;										// Set The X Direction
    	lightAngle.Y = 0.0f;										// Set The Y Direction
	    lightAngle.Z = 1.0f;										// Set The Z Direction

    	Normalize (lightAngle);										// Normalize The Light Direction

    	lightPos[0] = lightAngle.X;									// Set The X Direction ( NEW )
	    lightPos[1] = lightAngle.Y;									// Set The Y Direction ( NEW )
    	lightPos[2] = lightAngle.Z;									// Set The Z Direction ( NEW )
	    lightPos[3] = 0.0f;											// Set The W Coordinate. Because It Is Zero, The XYZ Coordinates Represent A Direction Instead Of A Position ( NEW )
    	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);				// Set Light Position. Because the ModelView Matrix is Identity Now, these Coordinates are Equivalent to Eye-Space Coordinates ( NEW )
    }

    // Setup the textures
    if (!ReadTextures())
    {
	    return false;
    }

    // Setup the mesh
	return ReadMesh ();
}


bool Initialize(void)											// Any Application & User Initialization Code Goes Here
{
	AppStatus.Visible		= true;								// At The Beginning, Our App Is Visible
	AppStatus.MouseFocus	= true;								// And Have Both Mouse
	AppStatus.KeyboardFocus = true;								// And Input Focus

	// Start Of User Initialization
    /*StartWhatIWant();*/

	return true;												// Return TRUE (Initialization Successful)
}


void Deinitialize(void)											// Any User Deinitialization Goes Here
{
	glDeleteTextures (1, &shaderTexture[0]);					// Delete The Shader Texture

#if defined(GL_ARB_vertex_program)
    if (rt_ARB_vertex_program)
    {
		glDeleteProgramsARB (3, &program[0]);					// Delete The Vertex And Fragment Programs ( NEW )
	}
#endif

#if defined(GL_ARB_vertex_buffer_object)
    if (rt_ARB_vertex_buffer_object)
    {
        glDeleteBuffersARB (1, &polyBufferObj[0]);              // Delete The Vertex Buffer Object ( NEW )
    }
#endif

	delete [] polyData;											// Delete The Polygon Data

	return;														// We Have Deinitilized Everything Now
}


void Update(Uint32 Milliseconds, Uint8 *Keys)					// Perform Motion Updates Here
{
    // Keyboard interaction
	if(Keys)													// If We're Sent A Key Event With The Update
	{
		if(Keys[SDLK_ESCAPE])									// And If The Key Pressed Was ESC
		{
			TerminateApplication();								// Terminate The Application
		}

		if(Keys[SDLK_F1])										// If The Key Pressed Was F1
		{
			ToggleFullscreen();									// Use SDL Function To Toggle Fullscreen Mode (But Not In Windows :) )
		}

	    if (Keys[SDLK_SPACE])									// Is the Space Bar Being Pressed?
    	{
		    modelRotate = !modelRotate;							// Toggle Model Rotation On/Off
	    	Keys[SDLK_SPACE] = false;
    	}

	    if (Keys[SDLK_KP1])									    // Is The Number 1 Being Pressed?
	    {
		    outlineDraw = !outlineDraw;							// Toggle Outline Drawing On/Off
		    Keys[SDLK_KP1] = false;
    	}

	    if (Keys[SDLK_KP2])									    // Is The Number 2 Being Pressed?
    	{
	    	outlineSmooth = !outlineSmooth;						// Toggle Anti-Aliasing On/Off
    		Keys[SDLK_KP2] = false;
	    }

    	if (Keys[SDLK_KP3])									    // Is The Number 3 Being Pressed?
	    {
    		useVertexProgram = !useVertexProgram;				// Toggle Vertex Programs On/Off
		    Keys[SDLK_KP3] = false;
	    }

    	if (Keys[SDLK_KP4])									    // Is The Number 4 Being Pressed?
	    {
    		useFragmentProgram = !useFragmentProgram;		    // Toggle Fragment Programs On/Off
		    Keys[SDLK_KP4] = false;
	    }

    	if (Keys[SDLK_KP5])									    // Is The Number 5 Being Pressed?
	    {
    		useBufferObject = !useBufferObject;				    // Toggle Vertex Buffer Objects On/Off
		    Keys[SDLK_KP5] = false;
	    }

    	if (Keys[SDLK_UP])								        // Is The Up Arrow Being Pressed?
	    {
    		outlineWidth++;										// Increase Line Width
    		Keys[SDLK_UP] = false;
	    }

    	if (Keys[SDLK_DOWN])								    // Is The Down Arrow Being Pressed?
	    {
    		outlineWidth--;										// Decrease Line Width
	    	Keys[SDLK_DOWN] = false;
    	}
	}

    // Animation
	if (modelRotate)											// Check To See If Rotation Is Enabled
		modelAngle += (float) (Milliseconds) / 10.0f;			// Update Angle Based On The Clock

	// Computing FPS
	// Display it every second
	// Assume each call to Update leads to a call to Draw, since Draw is the real "frame" of "FPS"
	{
		static unsigned int millisecondsInThatSecond = 0;
		static unsigned int frameCountInThatSecond = 0;
        static unsigned int firstTriangleInThatSecond = 0;

		millisecondsInThatSecond += Milliseconds;
		if
			(millisecondsInThatSecond > millisecondsBetweenCounters)	// Reached a second;
		{
			static char title[1024];

			sprintf(title, "%s - %5.2f fps ; %5.3f Mtps (Mtriangles/sec)",
                            APP_NAME,
                            frameCountInThatSecond / (millisecondsInThatSecond / 1000.f),
                            ((float)(allTrianglesEver - firstTriangleInThatSecond) / 1000000.f ) / (millisecondsInThatSecond / 1000.f)
                            );
			SDL_WM_SetCaption(title, NULL);

			millisecondsInThatSecond = 0;
			frameCountInThatSecond = 0;
            firstTriangleInThatSecond = allTrianglesEver;
		}
		frameCountInThatSecond++;
	}

	return;														// We Always Make Functions Return
}


int DrawCelShaded(void);    // Forward declaration

void DrawTextInfo(void);	// Forward declaration

void Draw(void)													// Our Drawing Code
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix
	glPushMatrix();												// Save The Modelview Matrix

    allTrianglesEver += DrawCelShaded();                        // It's show time !

    glPopMatrix();
    glPushMatrix();

    DrawTextInfo();						// Render texts

	glPopMatrix();												// Restore The Modelview Matrix

	return;														// We're Always Making Functions Return
}

int DrawCelShaded(void)    // Render the cel-shading model
{
	int i, j;													// Looping Variables

	float TmpShade;												// Temporary Shader Value

	MATRIX TmpMatrix;											// Temporary MATRIX Structure
	VECTOR TmpVector, TmpNormal;								// Temporary VECTOR Structures
	
	if (outlineSmooth)											// Check To See If We Want Anti-Aliased Lines
	{
		glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);				// Use The Good Calculations
		glEnable (GL_LINE_SMOOTH);								// Enable Anti-Aliasing
	}

	else														// We Don't Want Smooth Lines
		glDisable (GL_LINE_SMOOTH);								// Disable Anti-Aliasing

	glTranslatef (0.0f, 0.0f, -2.0f);							// Move 2 Units Away From The Screen
	glRotatef (modelAngle, 0.0f, 1.0f, 0.0f);					// Rotate The Model On It's Y-Axis

	glGetFloatv (GL_MODELVIEW_MATRIX, TmpMatrix.Data);			// Get The Generated Matrix

	// Cel-Shading Code //
#if defined(GL_ARB_vertex_program)
    if ((rt_ARB_vertex_program) && useVertexProgram)
	{
		glEnable(GL_VERTEX_PROGRAM_ARB);						// Enable Vertex Program ( NEW )
#if defined(GL_ARB_fragment_program)
        if ((rt_ARB_fragment_program) && useFragmentProgram)
	    {
            glEnable(GL_FRAGMENT_PROGRAM_ARB);                  // Enable Fragment Program ( NEW )
            glBindProgramARB(GL_VERTEX_PROGRAM_ARB, program[1]);// Use The Per-Pixel Specialized Vertex Program ( NEW )
        }
        else
#endif
        {
            glBindProgramARB(GL_VERTEX_PROGRAM_ARB, program[0]);// Use The Per-Vertex Specialized Vertex Program ( NEW )
        }
	}
#endif

	glEnable (GL_TEXTURE_1D);									// Enable 1D Texturing
	glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);			// Bind Our Texture

	glColor3f (1.0f, 1.0f, 1.0f);								// Set The Color Of The Model

	glEnable(GL_BLEND);

#if defined(GL_ARB_vertex_program) && defined(GL_ARB_vertex_buffer_object)
    if ((rt_ARB_vertex_program) && useVertexProgram && (rt_ARB_vertex_buffer_object) && useBufferObject)    // If vertex programs AND vertex buffer objects are supported AND demanded, draw using vertex array
    {
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, polyBufferObj[0]); // Bind Our Vertex Buffer Object

        glInterleavedArrays(GL_N3F_V3F, 0, NULL);               // Point to the polygon data, relatively to the vertex buffer object memory (thus the NULL pointer!) ( NEW )

        glDrawArrays(GL_TRIANGLES, 0, polyNum*3);               // Render out data using vertex arrays

        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);                // Bind The Default Vertex Buffer Object
    }
    else
#endif
    {
    	glBegin (GL_TRIANGLES);									// Tell OpenGL That We're Drawing Triangles

		for (i = 0; i < polyNum; i++)							// Loop Through Each Polygon
		{
			for (j = 0; j < 3; j++)								// Loop Through Each Vertex
			{
#if defined(GL_ARB_vertex_program)
                if ((rt_ARB_vertex_program) && useVertexProgram)// If We Use Vertex Programs, Send The Normal (And The Vertex Program Will Compute TexCoord) Otherwise Compute TexCoord In Software ( NEW )
				{
					glNormal3fv(&polyData[i].Verts[j].Nor.X);	// Send The Normal ( NEW )
				}
				else
#endif
				{
					TmpNormal.X = polyData[i].Verts[j].Nor.X;		// Fill Up The TmpNormal Structure With
					TmpNormal.Y = polyData[i].Verts[j].Nor.Y;		// The Current Vertices' Normal Values
					TmpNormal.Z = polyData[i].Verts[j].Nor.Z;

					RotateVector (TmpMatrix, TmpNormal, TmpVector);	// Rotate This By The Matrix

					Normalize (TmpVector);							// Normalize The New Normal

					TmpShade = DotProduct (TmpVector, lightAngle);	// Calculate The Shade Value

					if (TmpShade < 0.0f)
						TmpShade = 0.0f;							// Clamp The Value to 0 If Negative

					glTexCoord1f (TmpShade);						// Set The Texture Co-ordinate As The Shade Value
				}
				glVertex3fv (&polyData[i].Verts[j].Pos.X);			// Send The Vertex Position
			}
		}

    	glEnd ();													// Tell OpenGL To Finish Drawing
    }

	glDisable (GL_TEXTURE_1D);									// Disable 1D Textures

#if defined(GL_ARB_vertex_program)
    if ((rt_ARB_vertex_program) && useVertexProgram)
	{
		glDisable(GL_VERTEX_PROGRAM_ARB);						// Disable Vertex Program ( NEW )
#if defined(GL_ARB_fragment_program)
        if ((rt_ARB_fragment_program) && useFragmentProgram)
	    {
            glDisable(GL_FRAGMENT_PROGRAM_ARB);                 // Disable Fragment Program ( NEW )
        }
#endif
	}
#endif

	// Outline Code //
	if (outlineDraw)											// Check To See If We Want To Draw The Outline
	{
		glEnable (GL_BLEND);									// Enable Blending
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);		// Set The Blend Mode

		glPolygonMode (GL_BACK, GL_LINE);						// Draw Backfacing Polygons As Wireframes
		glLineWidth (outlineWidth);								// Set The Line Width

		glCullFace (GL_FRONT);									// Don't Draw Any Front-Facing Polygons

		glDepthFunc (GL_LEQUAL);								// Change The Depth Mode

		glColor3fv (&outlineColor[0]);							// Set The Outline Color

#if defined(GL_ARB_vertex_buffer_object)
        if ((rt_ARB_vertex_buffer_object) && useBufferObject)   // Use buffer objects is possible and wanted
        {
            glBindBufferARB(GL_ARRAY_BUFFER_ARB, polyBufferObj[0]);                 // Bind Our Vertex Buffer Object

            glInterleavedArrays(GL_V3F, sizeof(VERTEX), (void*)(sizeof(VECTOR)));   // Point to the polygon data, relatively to the vertex buffer object memory ( NEW )

            glDrawArrays(GL_TRIANGLES, 0, polyNum*3);                               // Render out data using vertex arrays

            glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);                                // Bind The Default Vertex Buffer Object
        }
        else
#endif
        {
    		glBegin (GL_TRIANGLES);								// Tell OpenGL What We Want To Draw

			for (i = 0; i < polyNum; i++)						// Loop Through Each Polygon
			{
				for (j = 0; j < 3; j++)							// Loop Through Each Vertex
				{
					glVertex3fv (&polyData[i].Verts[j].Pos.X);	// Send The Vertex Position
				}
			}

    		glEnd ();											// Tell OpenGL We've Finished
        }

		glDepthFunc (GL_LESS);									// Reset The Depth-Testing Mode

		glCullFace (GL_BACK);									// Reset The Face To Be Culled

		glPolygonMode (GL_BACK, GL_FILL);						// Reset Back-Facing Polygon Drawing Mode

		glDisable (GL_BLEND);									// Disable Blending
	}

    return polyNum + (outlineDraw ? polyNum : 0); // Return the number of triangles rendered.
}

void DrawTextAtPos(Texture& text, GLint xpos, GLint ypos, GLint xsize, GLint ysize)
{
	static const GLfloat constant = 8.f;
	text.bind();
	
	glBegin(GL_QUADS);
	glTexCoord2f(0.f,0.f);
	glVertex2f(-1.f+(GLfloat)xpos/constant,1.f-(GLfloat)ypos/constant);
	glTexCoord2f(1.f,0.f);
	glVertex2f(-1.f+(GLfloat)xpos/constant+(GLfloat)xsize/constant,1.f-(GLfloat)ypos/constant);
	glTexCoord2f(1.f,1.f);
	glVertex2f(-1.f+(GLfloat)xpos/constant+(GLfloat)xsize/constant,1.f-(GLfloat)ypos/constant-(GLfloat)ysize/constant);
	glTexCoord2f(0.f,1.f);
	glVertex2f(-1.f+(GLfloat)xpos/constant,1.f-(GLfloat)ypos/constant-(GLfloat)ysize/constant);
	glEnd();
}

void DrawTextInfo(void)
{
	glEnable(GL_BLEND);
	glBlendFunc(GL_ZERO, GL_SRC_COLOR);
	msgVP.bind();
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glLoadIdentity();
	glTranslatef(0.f,0.f,-2.f);
	glColor3f(1.f,1.f,1.f);

	// Draw text relatively to supported and/or enabled extensions
	DrawTextAtPos(msgVP, 0, 0, 8, 1);
	DrawTextAtPos(msgFP, 0, 1, 8, 1);
	DrawTextAtPos(msgVBO, 0, 2, 8, 1);
#if defined(GL_ARB_vertex_program)
	if (rt_ARB_vertex_program)
	{
		DrawTextAtPos(useVertexProgram ? msgEnabled : msgDisabled, 8, 0, 4, 1);
	}
	else
#endif
	{
		DrawTextAtPos(msgUnavail, 8, 0, 4, 1);
	}
#if defined(GL_ARB_fragment_program)
	if (rt_ARB_fragment_program)
	{
		DrawTextAtPos(useFragmentProgram ? msgEnabled : msgDisabled, 8, 1, 4, 1);
	}
	else
#endif
	{
		DrawTextAtPos(msgUnavail, 8, 1, 4, 1);
	}
#if defined(GL_ARB_vertex_buffer_object)
	if (rt_ARB_vertex_buffer_object)
	{
		DrawTextAtPos(useBufferObject ? msgEnabled : msgDisabled, 8, 2, 4, 1);
	}
	else
#endif
	{
		DrawTextAtPos(msgUnavail, 8, 2, 4, 1);
	}

	unsigned int line = 16;

	// Draw message concerning shading
#if defined(GL_ARB_vertex_program) && defined(GL_ARB_fragment_program)
	if ((rt_ARB_vertex_program) && (rt_ARB_fragment_program))
	{
		if (useFragmentProgram)
		{
			if (useVertexProgram)
			{
				DrawTextAtPos(msgShading[2], 0, --line, 16, 1);
			}
			else
			{
				DrawTextAtPos(msgRemark[1], 0, --line, 16, 1);
				DrawTextAtPos(msgShading[0], 0, --line, 16, 1);
			}
		}
		else
		{
			DrawTextAtPos(msgShading[useVertexProgram ? 1 : 0], 0, --line, 16, 1);
		}
	}
	else
#elif defined(GL_ARB_vertex_program)
	if (rt_ARB_vertex_program)
	{
		DrawTextAtPos(msgShading[useVertexProgram ? 1 : 0], 0, --line, 16, 1);
	}
	else
#elif defined(GL_ARB_fragment_program)
	if (rt_ARB_fragment_program)
	{
		if (useFragmentProgram)
		{
			DrawTextAtPos(msgRemark[1], 0, --line, 16, 1);
		}
		DrawTextAtPos(msgShading[0], 0, --line, 16, 1);
	}
	else
#endif
	{
		DrawTextAtPos(msgShading[0], 0, --line, 16, 1);
	}

	// Draw message concerning vertex storage for outlines
	if (outlineDraw)
	{
#if defined(GL_ARB_vertex_buffer_object)
		if (rt_ARB_vertex_buffer_object)
		{
			DrawTextAtPos(msgOutline[useBufferObject ? 1 : 0], 0, --line, 16, 1);
		}
		else
#endif
		{
			DrawTextAtPos(msgOutline[0], 0, --line, 16, 1);
		}
	}

	// Draw message concerning vertex storage for triangles
#if defined(GL_ARB_vertex_program) && defined(GL_ARB_vertex_buffer_object)
	if ((rt_ARB_vertex_program) && (rt_ARB_vertex_buffer_object))
	{
		if ((useBufferObject) && (!useVertexProgram))
		{
			DrawTextAtPos(msgRemark[0], 0, --line, 16, 1);
			DrawTextAtPos(msgVertex[0], 0, --line, 16, 1);
		}
		else
		{
			DrawTextAtPos(msgVertex[((useVertexProgram) && (useBufferObject)) ? 1 : 0], 0, --line, 16, 1);
		}
	}
	else
#elif defined(GL_ARB_vertex_buffer_object)
	if (rt_ARB_vertex_buffer_object)
	{
		DrawTextAtPos(msgRemark[0], 0, --line, 16, 1);
		DrawTextAtPos(msgVertex[0], 0, --line, 16, 1);
	}
	else
#endif
	{
		DrawTextAtPos(msgVertex[0], 0, --line, 16, 1);
	}

	glDisable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
}

